package com.away.fuckingworld.controller;


import com.away.fuckingworld.constant.VersionConfig;
import com.away.fuckingworld.entity.dto.AttachmentDTO;
import com.away.fuckingworld.entity.vo.ResultVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import org.apache.tika.io.IOUtils;
import org.apache.tika.mime.MediaType;
import org.apache.tika.parser.AutoDetectParser;
import org.springframework.util.unit.DataSize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.xml.sax.helpers.DefaultHandler;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


import org.apache.tika.metadata.HttpHeaders;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaMetadataKeys;
import org.apache.tika.metadata.HttpHeaders;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.metadata.TikaMetadataKeys;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.parser.Parser;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @author LHT
 * @date 2020-06-04
 */
@Api(description = "文件上传")
@RestController(VersionConfig.WEB_VERSION_URL)
@Slf4j
public class AttachmentController {



    @PostMapping("files")
    @ApiOperation(value = "附件上传通用接口", response = AttachmentDTO.class)
    public ResultVO<AttachmentDTO> fileUpload(@RequestParam("file") MultipartFile file) throws IOException {
        DataSize dataSize = DataSize.parse("50MB");
        Validate.isTrue(dataSize.toBytes() > 0, "decide.valid.upload.fileMaxSize.format");
        Validate.isTrue(file.getSize() <= dataSize.toBytes(), "decide.valid.upload.fileMaxSize", "50MB");
        Validate.isTrue(fileNameCheck(file.getOriginalFilename()), "system.file.length.name");
        Validate.isTrue(CheckFileType.checkFileType(file), "system.file.type.error");


        return ResultVO.success(new AttachmentDTO());
    }


    /**
     * 通过uuid从数据库中获取文件
     * @param uuid
     * @param request
     * @param response
     * @throws IOException
     */
    @GetMapping("file/{uuid}")
    @ApiOperation(value = "附件下载通用接口", response = AttachmentDTO.class)
    public void fileDownload(String uuid, HttpServletRequest request, HttpServletResponse response)  {

        String filename = "";
        response.setContentType(org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE);

        String agent = request.getHeader("User-Agent").toLowerCase();



        FileInputStream fin = null;
        ServletOutputStream out = null;
        try {
            fin = new FileInputStream("ss.txt");
             out = response.getOutputStream();

            if(StringUtils.containsAny(agent,"msie","trident","edge")){
                filename = URLEncoder.encode(filename,"UTF-8");//IE 浏览器
            }else{
                filename = new String(filename.getBytes("UTF-8"),"ISO8859-1");
            }
            response.addHeader("Content-Disposition","attachment; filename="+filename);

            IOUtils.copy(fin, out);
            out.flush();
        }catch (Exception e){
            log.error(e.getMessage());
        }finally {
            IOUtils.closeQuietly(fin);
            IOUtils.closeQuietly(out);
        }
        return ;
    }

    private Boolean fileNameCheck(String fileName) {
        if (fileName.contains("=")) {
            return false;
        }
        if (fileName.contains("!")) {
            return false;
        }
        if (fileName.contains("@")) {
            return false;
        }
        if (fileName.contains("#")) {
            return false;
        }
        if (fileName.contains("$")) {
            return false;
        }
        if (fileName.contains("%")) {
            return false;
        }
        if (fileName.contains("^")) {
            return false;
        }
        if (fileName.contains("&")) {
            return false;
        }
        if (fileName.contains("*")) {
            return false;
        }
        if (fileName.contains("?")) {
            return false;
        }
        if (fileName.contains("\\")) {
            return false;
        }
        if (fileName.contains(",")) {
            return false;
        }
        if (fileName.contains(";")) {
            return false;
        }
        if (fileName.contains(":")) {
            return false;
        }
        if (fileName.contains("..")) {
            return false;
        }
        if (fileName.contains("/")) {
            return false;
        }
        return true;
    }


}


class CheckFileType {

    public static final List<String> mFileTypeList = new ArrayList<>();

    public static final List<String> mFileNameTypeList = new ArrayList<>();

    static {
        mFileTypeList.add("application/msword");
        mFileTypeList.add("application/vnd.ms-powerpoint");
        mFileTypeList.add("application/vnd.ms-excel");
        mFileTypeList.add("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
        mFileTypeList.add("application/vnd.openxmlformats-officedocument.presentationml.presentation");
        mFileTypeList.add("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        mFileTypeList.add("application/x-rar-compressed");
        mFileTypeList.add("application/x-tika-ooxml");
        mFileTypeList.add("application/zip");
        mFileTypeList.add("application/pdf");
        mFileTypeList.add("video/.*");
        mFileTypeList.add("image/.*");
        mFileTypeList.add("text/plain");

        mFileNameTypeList.add("doc");
        mFileNameTypeList.add("docx");
        mFileNameTypeList.add("ppt");
        mFileNameTypeList.add("pptx");
        mFileNameTypeList.add("xls");
        mFileNameTypeList.add("xlsx");
        mFileNameTypeList.add("jpg");
        mFileNameTypeList.add("png");
        mFileNameTypeList.add("rar");
        mFileNameTypeList.add("zip");
        mFileNameTypeList.add("pdf");
        mFileNameTypeList.add("gif");
        mFileNameTypeList.add("txt");
    }

    //文件类型获取
    public static String getMimeType(MultipartFile file) {
        AutoDetectParser parser = new AutoDetectParser();
        parser.setParsers(new HashMap<MediaType, Parser>());
        Metadata metadata = new Metadata();
        metadata.add(TikaMetadataKeys.RESOURCE_NAME_KEY, file.getName());
        try (InputStream stream = file.getInputStream()) {
            parser.parse(stream, new DefaultHandler(), metadata, new ParseContext());
        } catch (Exception e) {
            throw new RuntimeException();
        }
        return metadata.get(HttpHeaders.CONTENT_TYPE);
    }

    public static Boolean checkFileType(MultipartFile file) {
        checkFileName(file);
        String fileType = getMimeType(file);
        for (Iterator<String> it = mFileTypeList.iterator(); it.hasNext(); ) {
            Pattern p = Pattern.compile(it.next());
            Matcher m = p.matcher(fileType);
            if (m.matches() == true) {
                return true;
            }
        }
        return false;
    }

    private static void checkFileName(MultipartFile file) {
        Boolean flag = false;
        try {
            String px = file.getOriginalFilename().split("\\.")[1];

            for (Iterator<String> it = mFileNameTypeList.iterator(); it.hasNext(); ) {
                Pattern p = Pattern.compile(it.next());
                Matcher m = p.matcher(px);
                if (m.matches() == true) {
                    flag = true;
                    break;
                }
            }
        } catch (Exception e) {
            Validate.isTrue(false, "system.file.type.error");
        }
        Validate.isTrue(flag, "system.file.type.error");
    }
}
